////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Crytek Character Animation source code
//	
//	History:
//	10/9/2004 - Created by Ivo Herzeg <ivo@crytek.de>
//
//  Contains:
//  interface class to all motions  
/////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef _CRY_MODEL_ANIMTATION_CONTAINER_HDR_
#define _CRY_MODEL_ANIMTATION_CONTAINER_HDR_

#include <VectorMap.h>
#include "ModelSkeleton.h"
#include <NameCRCHelper.h>
#include "VectorSet.h"
#include "StlUtils.h"

class CModelMesh;
class CCharacterModel;
class CryCGALoader;
class CSkeletonAnim;
struct CGlobalHeaderDBA;
struct ModelAnimationHeader;
struct StrafeWeights4;
struct StrafeWeights6;
struct IdleStepWeights6;







	#define CRC_GEN gEnv->pSystem->GetCrc32Gen() 


enum {
	CAF_File,
	AIM_File,
	LMG_File,
};


struct DebugJoint
{
	DebugJoint() {
		m_idxParent	=	-1;
		m_RelativeQuat.SetIdentity();	
		m_AbsoluteQuat.SetIdentity();
	}

	int16 m_idxParent;					//index of the parent-joint
	QuatT	m_RelativeQuat;		//current relative rotation and position of animation
	QuatT	m_AbsoluteQuat;
};



//////////////////////////////////////////////////////////////////////////
// Custom hash map class. 
//////////////////////////////////////////////////////////////////////////
struct CHashMap_AmimNameCRC
{
	typedef std::map<uint32, size_t> NameHashMap;

	//----------------------------------------------------------------------------------
	// Returns the index of the animation from crc value
	//----------------------------------------------------------------------------------
	SPU_NO_INLINE size_t	GetValueCRC(uint32 crc) const
	{
		NameHashMap::const_iterator it = m_HashMap.find(crc);
		if (it == m_HashMap.end())
			return -1;
		return it->second;
	}

	// Returns the index of the animation from name. Name converted in lower case in this function
	size_t GetValue(const char* name) const
	{
		assert (strlen(name) < MAX_STATIC_CHARS);
		char pStringArray[MAX_STATIC_CHARS];
		NameCRCHelper::MakeLowercase(pStringArray, name);
		uint32 crc32 = CRC_GEN->GetCRC32(pStringArray);
		return GetValueCRC( crc32 );
	}

	bool InsertValue(uint32 crc32, size_t num)
	{
		bool res = m_HashMap.find(crc32) == m_HashMap.end();
		m_HashMap[crc32] = num;
		return res;
	}

	size_t GetAllocMemSize() const
	{
		return m_HashMap.size() * (sizeof(uint32) + sizeof(size_t));
	}

	size_t GetMapSize() const
	{
		return m_HashMap.size();
	}

	void GetMemoryUsage(class ICrySizer* pSizer) const
	{
		pSizer->AddObject(m_HashMap);
	}

	void Clear()
	{
		m_HashMap.clear();
	}
protected:
	NameHashMap		m_HashMap;
};



//! this structure contains info about loaded animations
struct ModelAnimationHeader
{
#ifdef STORE_ANIMATION_NAMES
	string m_Name;		// the name of the animation (not the name of the file) - unique per-model
#endif
	uint32 m_CRC32Name;			//hash value for searching animations
	int16 m_nGlobalAnimId;
	int16 m_nAssetType;
	string m_strAimPose1[2];

	size_t SizeOfAnimationHeader() const 
	{	
		size_t size = 0;//sizeof(ModelAnimationHeader);
#ifdef STORE_ANIMATION_NAMES
		size += m_Name.length(); 
#endif
		size += m_strAimPose1[0].capacity(); 
		size += m_strAimPose1[1].capacity(); 
		return size;
	}

	void GetMemoryUsage( ICrySizer *pSizer ) const 
	{			
#ifdef STORE_ANIMATION_NAMES
		pSizer->AddObject(m_Name);
#endif
		pSizer->AddObject(m_strAimPose1[0]);
		pSizer->AddObject(m_strAimPose1[1]);
	}

	ModelAnimationHeader() : m_nGlobalAnimId(-1),m_nAssetType(CAF_File){};
	~ModelAnimationHeader()	{}

	const char* GetAnimName() const 
	{ 
#ifdef STORE_ANIMATION_NAMES
		return m_Name.c_str(); 
#else
		extern const char* strEmpty; 
		return strEmpty;
#endif
	};

	void SetAnimName(const char* name) 
	{
#ifdef STORE_ANIMATION_NAMES
		m_Name = name; 
#endif
		assert (strlen(name) < MAX_STATIC_CHARS);
		char pStringArray[MAX_STATIC_CHARS];
		NameCRCHelper::MakeLowercase(pStringArray, name);
		m_CRC32Name = CRC_GEN->GetCRC32(pStringArray);
	};

};

//////////////////////////////////////////////////////////////////////////
// Implementation of ICryAnimationSet, holding the information about animations
// and bones for a single model. Animations also include the subclass of morph targets
//////////////////////////////////////////////////////////////////////////
class CAnimationSet : public IAnimationSet
{
public:
	CAnimationSet();
	~CAnimationSet();

	// gets called when the given animation (by global id) is unloaded.
	// the animation controls need to be unbound to free up the memory
	void Init();
	void Clear();
	// prepares to load the specified number of CAFs by reserving the space for the controller pointers
	void prepareLoadCAFs (unsigned nReserveAnimations);
	void prepareLoadANMs (unsigned nReserveAnimations);
	// tries to load the animation info if isn't present in memory; returns NULL if can't load
	const ModelAnimationHeader* GetModelAnimationHeader(int32 i) const;

	int32 FindAimposeByGlobalID(uint32 nGlobalIDAimPose)
	{
		uint32 numAnims = GetAnimationCount();
		for (uint32 nMID=0; nMID<numAnims; nMID++)
		{
			if (m_arrAnimations[nMID].m_nAssetType!=AIM_File)
				continue;
			if (m_arrAnimations[nMID].m_nGlobalAnimId==nGlobalIDAimPose)
				return 1;
		}
		return -1;
	}

	size_t SizeOfAnimationSet() const;
	void GetMemoryUsage(ICrySizer *pSizer) const;


	const char* GetFacialAnimationPathByName(const char* szName);
	int GetNumFacialAnimations();
	const char* GetFacialAnimationName(int index);
	const char* GetFacialAnimationPath(int index);

	// when the animinfo is given, it's used to set the values of the global animation as if the animation has already been loaded once -
	// so that the next time the anim info is available and there's no need to load the animation synchronously
	// Returns the global anim id of the file, or -1 if error
	int LoadFileCAF(const char * szFileName, const char * szAnimName);
	int LoadFileAIM(const char * szFileName, const char * szAnimName);
	int LoadFileANM(const char * szFileName, const char * szAnimName, DynArray<CControllerTCB>& m_LoadCurrAnimation, CryCGALoader* pCGA, uint32 unique_model_id );
	int LoadFileLMG(const char * szFileName, const char * szAnimName);

	int CreateOrSetAnimationLMG(const char* animationName, int globalAnimationLmgId);
	virtual int LoadLMGFromMemory(const char* resourceName, const char* szAnimName, XmlNodeRef xmlData);
	virtual LmgAnimationStatus IsAnimationValidForLMG(const char* lmgBlendCode, const char* szAnimationName);

	// Reuse an animation that is already loaded in the global animation set for this model
	void ReuseAnimation(const ModelAnimationHeader& header, const char* szStandupAnimType);
	bool IsStandupAnim(const char* szAnimName, CryFixedStringT<256>& typeName) const;

	IController* GAH_GetRootController(GlobalAnimationHeaderCAF& rGAH, uint32 globalID, const CCharacterModel* pModel, const char* pAnimName);

	//private:
	// This only loops over all local anims and calls the 'per-anim' function.
	// TODO: Maybe it could be removed and the 'per-anim' function called on global load or something instead.
	bool ComputeSelectionProperties(int32 localAnimID);
	//public:

	void VerifyLMGs();
	void VerifyLocomotionGroup(int animationID);
	LmgAnimationStatus IsAnimationValidForLMG(uint32 lmgBlendCode, const SCRCName& animationName);

	struct FacialAnimationEntry
	{
		FacialAnimationEntry(const string& name, const string& path): name(name), path(path) {}
		string name;
		string path;
		uint32 crc;
		operator const char*() const {return name.c_str();}

		void GetMemoryUsage( ICrySizer *pSizer ) const
		{
			pSizer->AddObject(name);
			pSizer->AddObject(path);
		}
	};


	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	uint32 numMorphTargets() const; 
	const char* GetNameMorphTarget (int nMorphTargetId);

	// Returns the index of the animation (bone-animations and morphtargets), -1 if there's no such animation	
	int GetAnimIDByName (const char* szAnimationName) const;

	const char* GetNameByAnimID ( int nAnimationId);


	//------------------------------------------------------------------------------

	virtual int GetAnimIDByCRC(uint32 animationCRC) const;
	virtual uint32 GetCRCByAnimID(int nAnimationId) const;
	virtual uint32 GetFilePathCRCByAnimID(int nAnimationId) const;

	// for internal use only
	SPU_INDIRECT(CommandBufferExecute(M))
		uint32 GetAnimationCount() const { return m_arrAnimations.size(); }

	GlobalAnimationHeaderCAF* GetGAH_CAF(int nAnimationId) const;
	GlobalAnimationHeaderCAF* GetGAH_CAF(const char* AnimationName) const;
	GlobalAnimationHeaderAIM* GetGAH_AIM(int nAnimationId);
	GlobalAnimationHeaderAIM* GetGAH_AIM(const char* AnimationName);
	GlobalAnimationHeaderLMG* GetGAH_LMG(int nAnimationId);
	GlobalAnimationHeaderLMG* GetGAH_LMG(const char* AnimationName);

	float GetStart (int nAnimationId);
	f32 GetSpeed(int nAnimationId);
	f32 GetSlope(int nAnimationId);
	Vec2 GetMinMaxSpeedAsset_msec(int32 animID );

	LMGCapabilities GetLMGPropertiesByName(const char* name, Vec2& vStrafeDirection, f32 fDesiredTurn, f32 fSlope  ); 

	f32 GetIWeightForSpeed(int nAnimationId, f32 Speed);
	f32 GetDuration_sec(int nAnimationId);
	uint32 GetAnimationFlags(int nAnimationId);
	uint32 GetBlendSpaceCode(int nAnimationId);

	CryAnimationPath GetAnimationPath(const char* szAnimationName); 
	CryAnimationPath GetAnimationPath( int AnimID );

	const QuatT& GetAnimationStartLocation(const char* szAnimationName); 
	const QuatT& GetAnimationStartLocation( int32 AnimID );

	VIRTUAL const SAnimationSelectionProperties* GetAnimationSelectionProperties(const char* szAnimationName);
	VIRTUAL const SAnimationSelectionProperties* GetAnimationSelectionProperties( int32 localAnimID );

	const QuatT& GetJointStartLocation(const char* szAnimationName, int32 jointCRC32);


	void CreateSkeletonArray( std::vector< std::vector<DebugJoint> >& arrSkeletons, uint32 nAnimID );


	const char* GetFilePathByName (const char* szAnimationName) const;
	const char* GetFilePathByID(int nAnimationId) const;

	uint32 GetAnimationSize(const uint32 nAnimationId) const;
	uint32 GetTotalPosKeys(const uint32 nAnimationId ) const;
	uint32 GetTotalRotKeys(const uint32 nAnimationId ) const;
	bool   ExportCAF2HTR(const char* szAnimationName, const char* savePath, const ISkeletonAnim* m_pSkeletonAnim);
	const char* GetDBAFileName(const uint32 nAnimationId) const;

	int32 GetGlobalIDByName(const char* szAnimationName);
	int32 GetGlobalIDByAnimID(int nAnimationId);
	ILINE uint32 GetGlobalIDByAnimID_Fast(int nAnimationId) const 
	{ 
		assert(nAnimationId>=0); 
		assert(nAnimationId<m_arrAnimations.size()); 
		return m_arrAnimations[nAnimationId].m_nGlobalAnimId; 
	};
	f32 GetClosestQuatInChannel(const char* szAnimationName,int32 JointID, const Quat& q);




	typedef VectorSet<FacialAnimationEntry, stl::less_stricmp<FacialAnimationEntry> > FacialAnimationSet;
	FacialAnimationSet& GetFacialAnimations() {return m_facialAnimations;}

	const char *GetFnPAnimGroupName(int idx) { return idx<m_arrStandupAnimTypes.size() ? m_arrStandupAnimTypes[idx]:0; }

	// Not safe method. Just direct access to m_arrAnimations
	const ModelAnimationHeader&  GetModelAnimationHeaderRef(int i)
	{
		return m_arrAnimations[i];
	}

	CCharacterModel* m_pModel;	
	uint32 m_CharEditMode;
	DynArray< DynArray<int> > m_arrStandupAnims;
	DynArray<string> m_arrStandupAnimTypes;

private:
	void ProcessStandupAnim(const char* szType, int nLocalAnimId2);

	void StoreAnimName(uint32 nameCRC, const char* name)
	{
		if (Console::GetInst().ca_StoreAnimNamesOnLoad)
			m_hashToNameMap[nameCRC] = name;
	}

	// No more direct access to vector. No more LocalIDs!!!! This is just vector of registered animations!!!
	// When animation started we need build remap controllers\bones
	DynArray<ModelAnimationHeader> m_arrAnimations;
	FacialAnimationSet m_facialAnimations;
	CHashMap_AmimNameCRC m_AnimationHashMap;

	typedef std::map<uint32,string> HashToNameMap;
	HashToNameMap m_hashToNameMap;						// Simple optional array of names that maps to m_arrAnimations. Filled if ca_StoreAnimNamesOnLoad == 1.
};



#endif
